using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using UMC.Net;
using UMC.Data;
using System.Globalization;
using System.IO;
using System.IO.Compression;

namespace UMC.ITME
{
    class CopyStream : System.IO.Stream
    {
        Stream[] _streams;
        Action _close;
        public CopyStream(Action action, params Stream[] streams)
        {
            this._close = action;
            _streams = streams;
        }
        public override bool CanRead => throw new NotImplementedException();

        public override bool CanSeek => throw new NotImplementedException();

        public override bool CanWrite => true;

        public override long Length => throw new NotImplementedException();

        public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public override void Flush()
        {
            foreach (var s in this._streams)
            {
                s.Flush();
            }
            // throw new NotImplementedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            foreach (var s in this._streams)
            {
                s.Write(buffer, offset, count);
            }
        }
        public override void Close()
        {
            foreach (var s in this._streams)
            {
                s.Close();
            }
            _close();
        }
    }
    class HttpReadBody : System.IO.Stream
    {
        class Copy : HttpReadBody
        {
            NetWriteData _writeData;
            public Copy(NetWriteData writeData, Action<Stream> reader, Action<Stream> finish, Action error) : base(reader, finish, error)
            {
                this._writeData = writeData;
            }
            protected override void SetBuffer(byte[] buffer, int offset, int count)
            {
                base.SetBuffer(buffer, offset, count);
                _writeData(buffer, offset, count);
            }
        }
        public static HttpReadBody ReadBody(NetWriteData writeData, Action<Stream> reader, Action<Stream> finish, Action error)
        {
            return new Copy(writeData, reader, finish, error);
        }
        void Decompress(StringValue encoding)
        {
            if (encoding.Span.SequenceEqual("gzip"u8))
            {
                _Stream = new GZipStream(this, CompressionMode.Decompress);
            }
            else if (encoding.Span.SequenceEqual("deflate"u8))
            {
                _Stream = new DeflateStream(this, CompressionMode.Decompress);
            }
            else if (encoding.Span.SequenceEqual("br"u8))
            {
                _Stream = new BrotliStream(this, System.IO.Compression.CompressionMode.Decompress);

            }
            else
            {
                _Stream = this;
            }
        }
        Stream _Stream;
        public HttpReadBody(Action<Stream> reader, Action<Stream> finish, Action error)
        {
            this._error = error;
            this._finish = finish;
            this._reader = reader;
        }
        Memory<byte> _Buffer = Memory<byte>.Empty;
        protected virtual void SetBuffer(byte[] buffer, int offset, int count)
        {
            _Position = 0;
            _Buffer = new Memory<byte>(buffer, offset, count);
        }
        int _Position = 0;
        public override bool CanRead => true;

        public override bool CanSeek => throw new NotImplementedException();

        public override bool CanWrite => throw new NotImplementedException();

        public override long Length => throw new NotImplementedException();
        // int 
        public override long Position { get => _Position; set => throw new NotImplementedException(); }

        public override void Flush()
        {

        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            int size = _Buffer.Length - _Position;
            if (size > 0)
            {
                if (size > count)
                {
                    _Buffer.Span.Slice(_Position, count).CopyTo(buffer.AsSpan(offset, count));
                    _Position += count;
                    return count;
                }
                else
                {
                    _Buffer.Span.Slice(_Position, size).CopyTo(buffer.AsSpan(offset, size));
                    _Buffer = Memory<byte>.Empty;
                    _Position = 0;
                    return size;

                }
            }
            return 0;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        Action _error;
        Action<Stream> _finish, _reader;
        public override void Write(byte[] b, int i, int c)
        {
            throw new NotImplementedException();
        }
        public void Append(NetHttpResponse xhr)
        {
            this.Decompress(xhr.Headers.Get(StringValue.ContentEncoding.Span));
            xhr.ReadAsData(this.Append);
        }
        void Append(byte[] b, int i, int c)
        {
            if (c == 0 && b.Length == 0)
            {
                if (i == -1)
                {
                    _error();

                }
                else
                {
                    this._finish(_Stream);
                }
                _Stream.Dispose();
            }
            else
            {
                this.SetBuffer(b, i, c);
                _reader(_Stream);
            }
        }
    }
    public class CookieSet
    {
        Set<Cookie> _cvs = new Set<Cookie>(Comparer<Cookie>.Create((x, y) =>
        {
            int c = x.Name.Span.SequenceCompareTo(y.Name.Span);
            if (c != 0)
            {
                return c;
            }
            c = x.Path.Span.SequenceCompareTo(y.Path.Span);
            if (c != 0)
            {
                return c;
            }
            return x.Domain.Span.SequenceCompareTo(y.Domain.Span);
        }));
        public class Cookie
        {
            public StringValue Name
            {
                get; set;
            } = StringValue.Empty;
            public StringValue Value
            {
                get; set;
            } = StringValue.Empty;
            public StringValue Path
            {
                get; set;
            } = StringValue.Empty;
            public StringValue Domain
            {
                get; set;
            } = StringValue.Empty;
            public uint Expires { get; set; }
        }

        static StringValue Convert(StringValue stringValue, ReadOnlySpan<byte> bytes)
        {
            int domainIndex = stringValue.Span.IndexOf(bytes, true) + bytes.Length;
            if (domainIndex > bytes.Length)
            {

                int v = stringValue.Span.IndexOf(";"u8, false, domainIndex);
                if (v > 0)
                {
                    return stringValue.Slice(domainIndex, v - domainIndex);
                }
                else
                {
                    return stringValue.Slice(domainIndex);//, v - domainIndex);

                }

            }
            return StringValue.Empty;
        }
        public void Append(Cookie cookie)
        {
            IsChange = true;
            _cvs.Put(cookie);
        }
        public bool IsChange
        {
            get; set;
        }

        static bool TryParse(StringValue s, out DateTime result)
        {
            if (s.Span.EndsWith("GMT"u8))
            {
                int l = s.Span.LastIndexOf((byte)'-');
                if (l > 0)
                {
                    var sp = s.Slice(l + 1).Span.IndexOf((byte)' ');

                    if (sp == 2)
                    {
                        string format = "ddd, dd-MMM-yy HH:mm:ss GMT";
                        DateTimeStyles style = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal;

                        if (DateTime.TryParseExact(s.ToString(), format, CultureInfo.InvariantCulture, style, out result))
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    else if (sp == 4)
                    {
                        string format = "ddd, dd-MMM-yyyy HH:mm:ss GMT";
                        DateTimeStyles style = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal;

                        if (DateTime.TryParseExact(s.ToString(), format, CultureInfo.InvariantCulture, style, out result))
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    var sp = s.Span.IndexOf((byte)':') - 3;
                    if (sp > 0)
                    {
                        var en = s.Slice(0, sp).Span.LastIndexOf((byte)' ');
                        switch (sp - en)
                        {
                            case 3:
                                {
                                    string format = "ddd, dd MMM yy HH:mm:ss GMT";
                                    DateTimeStyles style = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal;

                                    if (DateTime.TryParseExact(s.ToString(), format, CultureInfo.InvariantCulture, style, out result))
                                    {
                                        return true;
                                    }
                                    else
                                    {
                                        return false;
                                    }
                                }
                            case 5:
                                {
                                    string format = "ddd, dd MMM yyyy HH:mm:ss GMT";
                                    DateTimeStyles style = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal;

                                    if (DateTime.TryParseExact(s.ToString(), format, CultureInfo.InvariantCulture, style, out result))
                                    {
                                        return true;
                                    }
                                    else
                                    {
                                        return false;
                                    }
                                }
                        }
                    }
                    result = DateTime.MinValue;
                    return false;

                }
            }

            if (DateTime.TryParse(s.ToString(), out result))
            {
                return true;
            }
            return false;
        }
        public static bool TryConvert(StringValue stringValue, out Cookie cookie)
        {
            var nIndex = stringValue.Span.IndexOf("="u8);
            var vIndex = stringValue.Span.IndexOf(";"u8);
            cookie = null;
            if (vIndex > 0 && nIndex > 0)
            {
                cookie = new Cookie();
                cookie.Name = stringValue.Slice(0, nIndex);
                cookie.Value = stringValue.Slice(nIndex + 1, vIndex - nIndex - 1);
                cookie.Domain = Convert(stringValue, "; Domain="u8);
                cookie.Path = Convert(stringValue, "; Path="u8);
                var Expires = Convert(stringValue, "; Expires="u8);

                if (Expires.IsEmpty == false)
                {
                    if (TryParse(Expires, out var ex))
                    {
                        cookie.Expires = Utility.TimeSpan(ex) + 1;
                    }
                }
                var Max = Convert(stringValue, "; Max-Age="u8);

                if (Max.IsEmpty == false)
                {
                    if (Utility.TryParse(Max.Span, 10, out var max))
                    {

                        cookie.Expires = Utility.TimeSpan() + (uint)max;
                    }
                }

                return true;

            }
            return false;
        }
        static byte[] Check(byte[] stringValue, int index, int l)
        {
            if (l + index > stringValue.Length)
            {
                var bs = System.Buffers.ArrayPool<byte>.Shared.Rent(stringValue.Length + l + 512);
                Array.Copy(stringValue, bs, stringValue.Length);
                System.Buffers.ArrayPool<byte>.Shared.Return(stringValue);
                return bs;
            }
            return stringValue;
        }
        static int CheckValue(Span<byte> bytes, byte type, StringValue stringValue)
        {
            if (stringValue.Length > 0)
            {
                bytes[0] = type;
                BitConverter.TryWriteBytes(bytes.Slice(1, 2), (Int16)stringValue.Length);
                stringValue.Span.CopyTo(bytes.Slice(3));
                return stringValue.Length + 3;
            }
            return 0;
        }
        public Cookie[] GetCookies(StringValue name, StringValue host, StringValue path)
        {
            var ls = new List<Cookie>();
            foreach (var cookie in _cvs.Span)
            {
                if (cookie.Name.Span.SequenceEqual(name.Span))
                {
                    if (cookie.Domain.IsEmpty == false)
                    {
                        if (host.Length > cookie.Domain.Length)
                        {
                            if (path.Span.StartsWith(cookie.Path.Span))
                            {
                                ls.Add(cookie);
                            }
                        }
                        else if (host.Length == cookie.Domain.Length - 1 && cookie.Domain.Span[0] == HttpReplacer.BytePOINT)
                        {
                            if (cookie.Domain.Span.EndsWith(host.Span))
                            {
                                if (path.Span.StartsWith(cookie.Path.Span))
                                {
                                    ls.Add(cookie);
                                }
                            }

                        }
                    }
                }
            }
            return ls.ToArray();
        }
        public void WirteDomain(HeadValues cookies, StringValue host, StringValue path)
        {
            var now = Utility.TimeSpan();
            foreach (var cookie in _cvs.Span)
            {
                if (cookie.Name?.IsEmpty == false)
                {
                    if (cookie.Expires > 0 && cookie.Expires < now)
                    {
                        continue;

                    }
                    else if (cookie.Domain.IsEmpty == false)
                    {
                        if (host.Length > cookie.Domain.Length)
                        {
                            if (host.Span.EndsWith(cookie.Domain.Span))
                            {
                                if (path.Span.StartsWith(cookie.Path.Span))
                                {
                                    cookies[cookie.Name] = cookie.Value;
                                }
                            }
                        }
                        else if (cookie.Domain.Span[0] == HttpReplacer.BytePOINT)
                        {
                            if (host.Length == cookie.Domain.Length - 1 && cookie.Domain.Span.EndsWith(host.Span))
                            {
                                if (path.Span.StartsWith(cookie.Path.Span))
                                {
                                    cookies[cookie.Name] = cookie.Value;
                                }
                            }
                        }
                        else if (cookie.Domain.Span.SequenceEqual(host.Span))
                        {
                            cookies[cookie.Name] = cookie.Value;
                        }
                    }

                }
            }
        }
        public void Clear()
        {
            _cvs.Clear();
        }
        public void WirteValue(HeadValues cookies, StringValue host, StringValue path)
        {
            var now = Utility.TimeSpan();
            foreach (var cookie in _cvs.Span)
            {
                if (cookie.Name?.IsEmpty == false)
                {
                    if (cookie.Expires > 0 && cookie.Expires < now)
                    {
                        continue;

                    }
                    else if (cookie.Domain.IsEmpty || host.Span.EndsWith(cookie.Domain.Span))
                    {
                        if (path.Span.StartsWith(cookie.Path.Span))
                        {
                            cookies[cookie.Name] = cookie.Value;
                        }
                    }

                }
            }

        }
        public void ClearExpires()
        {
            var now = Utility.TimeSpan();
            var ls = _cvs.Span.ToArray().Where(r => r.Expires == 0 || r.Expires < now);
            foreach (var l in ls)
            {
                this.IsChange = true;
                _cvs.Remove(l);
            }
        }
        public byte[] ToBytes()
        {
            var bytes = System.Buffers.ArrayPool<byte>.Shared.Rent(512);// new List<byte>();
            int size = 0;
            try
            {
                var now = Utility.TimeSpan();
                foreach (var cookie in _cvs.Span)
                {
                    if (cookie.Name?.IsEmpty == false)
                    {
                        if (cookie.Expires > 0 && cookie.Expires < now)
                        {
                            continue;

                        }
                        bytes = Check(bytes, size, cookie.Name.Length + 4);
                        size += CheckValue(bytes.AsSpan(size), 1, cookie.Name);

                        bytes = Check(bytes, size, cookie.Value.Length + 4);
                        size += CheckValue(bytes.AsSpan(size), 2, cookie.Value);

                        bytes = Check(bytes, size, cookie.Path.Length + 4);
                        size += CheckValue(bytes.AsSpan(size), 3, cookie.Path);

                        bytes = Check(bytes, size, cookie.Domain.Length + 4);
                        size += CheckValue(bytes.AsSpan(size), 4, cookie.Domain);

                        if (cookie.Expires > 0)
                        {
                            bytes = Check(bytes, size, 6);
                            bytes[size] = 5;
                            size++;
                            BitConverter.TryWriteBytes(bytes.AsSpan(size, 4), cookie.Expires);
                            size += 4;
                        }
                        bytes[size] = 0;
                        size++;

                    }
                }
                return bytes.AsSpan(0, size).ToArray();
            }
            finally
            {

                System.Buffers.ArrayPool<byte>.Shared.Return(bytes);
            }
        }
        public CookieSet(byte[] bytes)
        {
            if (bytes?.Length > 0)
            {
                int index = 0;
                var cookie = new Cookie();
                var now = Utility.TimeSpan();
                while (index < bytes.Length)
                {
                    switch (bytes[index])
                    {
                        case 0:
                            if (cookie.Name?.IsEmpty == false)
                            {
                                if (cookie.Expires > 0 && cookie.Expires < now)
                                {
                                    this.IsChange = true;
                                }
                                else
                                {
                                    _cvs.Put(cookie);
                                }
                            }
                            index++;
                            cookie = new Cookie();
                            break;
                        case 1:
                            {
                                index++;
                                var len = BitConverter.ToInt16(bytes, index);
                                index += 2;
                                cookie.Name = new StringValue(bytes, index, len);
                                index += len;
                            }
                            break;
                        case 2:
                            {
                                index++;
                                var len = BitConverter.ToInt16(bytes, index);
                                index += 2;
                                cookie.Value = new StringValue(bytes, index, len);
                                index += len;
                            }
                            break;
                        case 3:
                            {
                                index++;
                                var len = BitConverter.ToInt16(bytes, index);
                                index += 2;
                                cookie.Path = new StringValue(bytes, index, len);
                                index += len;
                            }
                            break;
                        case 4:
                            {
                                index++;
                                var len = BitConverter.ToInt16(bytes, index);
                                index += 2;
                                cookie.Domain = new StringValue(bytes, index, len);
                                index += len;
                            }
                            break;
                        case 5:
                            {
                                index++;
                                cookie.Expires = BitConverter.ToUInt32(bytes, index);
                                index += 4;
                            }
                            break;
                        default:
                            return;//.ToArray();
                    }

                }
            }
        }
    }
}
//     internal static class Libsodium
//     {
//         [StructLayout(LayoutKind.Explicit, Size = 208)]
//         internal struct crypto_auth_hmacsha256_state
//         {
//         }

//         [StructLayout(LayoutKind.Explicit, Size = 416)]
//         internal struct crypto_auth_hmacsha512_state
//         {
//         }

//         [StructLayout(LayoutKind.Explicit, Size = 384)]
//         internal struct crypto_generichash_blake2b_state
//         {
//         }

//         [StructLayout(LayoutKind.Explicit, Size = 104)]
//         internal struct crypto_hash_sha256_state
//         {
//         }

//         [StructLayout(LayoutKind.Explicit, Size = 208)]
//         internal struct crypto_hash_sha512_state
//         {
//         }

//         [StructLayout(LayoutKind.Explicit, Size = 32)]
//         internal readonly struct PublicKeyBytes
//         {
//         }

//         internal sealed class SecureMemoryHandle : SafeHandle
//         {
//             private int _size;

//             public override bool IsInvalid => handle == IntPtr.Zero;

//             public int Size => _size;

//             public SecureMemoryHandle()
//                 : base(IntPtr.Zero, ownsHandle: true)
//             {
//             }

//             public static SecureMemoryHandle Create(int size)
//             {
//                 SecureMemoryHandle secureMemoryHandle = sodium_malloc(checked((nuint)size));
//                 if (secureMemoryHandle.IsInvalid)
//                 {
//                     throw new OutOfMemoryException();
//                 }

//                 secureMemoryHandle._size = size;
//                 return secureMemoryHandle;
//             }

//             public static SecureMemoryHandle CreateFrom(ReadOnlySpan<byte> source)
//             {
//                 SecureMemoryHandle secureMemoryHandle = Create(source.Length);
//                 bool success = false;
//                 try
//                 {
//                     secureMemoryHandle.DangerousAddRef(ref success);
//                     source.CopyTo(secureMemoryHandle.DangerousGetSpan());
//                 }
//                 finally
//                 {
//                     if (success)
//                     {
//                         secureMemoryHandle.DangerousRelease();
//                     }
//                 }

//                 return secureMemoryHandle;
//             }

//             public void CopyTo(Span<byte> destination)
//             {
//                 bool success = false;
//                 try
//                 {
//                     DangerousAddRef(ref success);
//                     DangerousGetSpan().CopyTo(destination);
//                 }
//                 finally
//                 {
//                     if (success)
//                     {
//                         DangerousRelease();
//                     }
//                 }
//             }

//             public unsafe Span<byte> DangerousGetSpan()
//             {
//                 return new Span<byte>((void*)handle, _size);
//             }

//             protected override bool ReleaseHandle()
//             {
//                 sodium_free(handle);
//                 return true;
//             }
//         }

//         internal const int crypto_aead_aes256gcm_ABYTES = 16;

//         internal const int crypto_aead_aes256gcm_KEYBYTES = 32;

//         internal const int crypto_aead_aes256gcm_NPUBBYTES = 12;

//         internal const int crypto_aead_aes256gcm_NSECBYTES = 0;

//         internal const int crypto_aead_chacha20poly1305_ietf_ABYTES = 16;

//         internal const int crypto_aead_chacha20poly1305_ietf_KEYBYTES = 32;

//         internal const int crypto_aead_chacha20poly1305_ietf_NPUBBYTES = 12;

//         internal const int crypto_aead_chacha20poly1305_ietf_NSECBYTES = 0;

//         internal const int crypto_aead_xchacha20poly1305_ietf_ABYTES = 16;

//         internal const int crypto_aead_xchacha20poly1305_ietf_KEYBYTES = 32;

//         internal const int crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = 24;

//         internal const int crypto_aead_xchacha20poly1305_ietf_NSECBYTES = 0;

//         internal const int crypto_auth_hmacsha256_BYTES = 32;

//         internal const int crypto_auth_hmacsha256_KEYBYTES = 32;

//         internal const int crypto_auth_hmacsha512_BYTES = 64;

//         internal const int crypto_auth_hmacsha512_KEYBYTES = 32;

//         internal const int crypto_generichash_blake2b_BYTES = 32;

//         internal const int crypto_generichash_blake2b_BYTES_MAX = 64;

//         internal const int crypto_generichash_blake2b_BYTES_MIN = 16;

//         internal const int crypto_generichash_blake2b_KEYBYTES = 32;

//         internal const int crypto_generichash_blake2b_KEYBYTES_MAX = 64;

//         internal const int crypto_generichash_blake2b_KEYBYTES_MIN = 16;

//         internal const int crypto_hash_sha256_BYTES = 32;

//         internal const int crypto_hash_sha512_BYTES = 64;

//         internal const int crypto_pwhash_argon2i_ALG_ARGON2I13 = 1;

//         internal const int crypto_pwhash_argon2i_BYTES_MIN = 16;

//         internal const long crypto_pwhash_argon2i_MEMLIMIT_MIN = 8192L;

//         internal const long crypto_pwhash_argon2i_OPSLIMIT_MAX = 4294967295L;

//         internal const long crypto_pwhash_argon2i_OPSLIMIT_MIN = 3L;

//         internal const int crypto_pwhash_argon2i_SALTBYTES = 16;

//         internal const int crypto_pwhash_argon2id_ALG_ARGON2ID13 = 2;

//         internal const int crypto_pwhash_argon2id_BYTES_MIN = 16;

//         internal const long crypto_pwhash_argon2id_MEMLIMIT_MIN = 8192L;

//         internal const long crypto_pwhash_argon2id_OPSLIMIT_MAX = 4294967295L;

//         internal const long crypto_pwhash_argon2id_OPSLIMIT_MIN = 1L;

//         internal const int crypto_pwhash_argon2id_SALTBYTES = 16;

//         internal const int crypto_pwhash_scryptsalsa208sha256_BYTES_MIN = 16;

//         internal const long crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN = 16777216L;

//         internal const long crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX = 4294967295L;

//         internal const long crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN = 32768L;

//         internal const int crypto_pwhash_scryptsalsa208sha256_SALTBYTES = 32;

//         internal const int crypto_scalarmult_curve25519_BYTES = 32;

//         internal const int crypto_scalarmult_curve25519_SCALARBYTES = 32;

//         internal const string crypto_secretbox_PRIMITIVE = "xsalsa20poly1305";

//         internal const int crypto_secretbox_xsalsa20poly1305_KEYBYTES = 32;

//         internal const int crypto_secretbox_xsalsa20poly1305_MACBYTES = 16;

//         internal const int crypto_secretbox_xsalsa20poly1305_NONCEBYTES = 24;

//         internal const int crypto_sign_ed25519_BYTES = 64;

//         internal const int crypto_sign_ed25519_PUBLICKEYBYTES = 32;

//         internal const int crypto_sign_ed25519_SECRETKEYBYTES = 64;

//         internal const int crypto_sign_ed25519_SEEDBYTES = 32;

//         internal const int crypto_stream_chacha20_ietf_KEYBYTES = 32;

//         internal const int crypto_stream_chacha20_ietf_NONCEBYTES = 12;

//         internal const int SODIUM_LIBRARY_VERSION_MAJOR = 10;

//         internal const int SODIUM_LIBRARY_VERSION_MINOR = 3;

//         internal const string SODIUM_VERSION_STRING = "1.0.18";

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_aes256gcm_abytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_aes256gcm_decrypt(byte* m, out ulong mlen_p, byte* nsec, byte* c, ulong clen, byte* ad, ulong adlen, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_aes256gcm_encrypt(byte* c, out ulong clen_p, byte* m, ulong mlen, byte* ad, ulong adlen, byte* nsec, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int crypto_aead_aes256gcm_is_available();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_aes256gcm_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_aes256gcm_npubbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_aes256gcm_nsecbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_chacha20poly1305_ietf_abytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_chacha20poly1305_ietf_decrypt(byte* m, out ulong mlen_p, byte* nsec, byte* c, ulong clen, byte* ad, ulong adlen, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_chacha20poly1305_ietf_encrypt(byte* c, out ulong clen_p, byte* m, ulong mlen, byte* ad, ulong adlen, byte* nsec, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_chacha20poly1305_ietf_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_chacha20poly1305_ietf_npubbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_chacha20poly1305_ietf_nsecbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_xchacha20poly1305_ietf_abytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_xchacha20poly1305_ietf_decrypt(byte* m, out ulong mlen_p, byte* nsec, byte* c, ulong clen, byte* ad, ulong adlen, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_aead_xchacha20poly1305_ietf_encrypt(byte* c, out ulong clen_p, byte* m, ulong mlen, byte* ad, ulong adlen, byte* nsec, byte* npub, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_xchacha20poly1305_ietf_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_xchacha20poly1305_ietf_npubbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_aead_xchacha20poly1305_ietf_nsecbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha256_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha256_final(crypto_auth_hmacsha256_state* state, byte* @out);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha256_init(crypto_auth_hmacsha256_state* state, byte* key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha256_init(crypto_auth_hmacsha256_state* state, SecureMemoryHandle key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha256_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha256_statebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha256_update(crypto_auth_hmacsha256_state* state, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha256_update(crypto_auth_hmacsha256_state* state, SecureMemoryHandle @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha512_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha512_final(crypto_auth_hmacsha512_state* state, byte* @out);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha512_init(crypto_auth_hmacsha512_state* state, byte* key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha512_init(crypto_auth_hmacsha512_state* state, SecureMemoryHandle key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha512_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_auth_hmacsha512_statebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha512_update(crypto_auth_hmacsha512_state* state, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_auth_hmacsha512_update(crypto_auth_hmacsha512_state* state, SecureMemoryHandle @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int sodium_init();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int sodium_set_misuse_handler(Action handler);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b(byte* @out, nuint outlen, byte* @in, ulong inlen, IntPtr key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b(byte* @out, nuint outlen, byte* @in, ulong inlen, SecureMemoryHandle key, nuint keylen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_bytes_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_bytes_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b_final(crypto_generichash_blake2b_state* state, byte* @out, nuint outlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b_init(crypto_generichash_blake2b_state* state, IntPtr key, nuint keylen, nuint outlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b_init(crypto_generichash_blake2b_state* state, SecureMemoryHandle key, nuint keylen, nuint outlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_keybytes_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_keybytes_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_generichash_blake2b_statebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_generichash_blake2b_update(crypto_generichash_blake2b_state* state, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha256(byte* @out, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_hash_sha256_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha256_final(crypto_hash_sha256_state* state, byte* @out);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha256_init(crypto_hash_sha256_state* state);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_hash_sha256_statebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha256_update(crypto_hash_sha256_state* state, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha512(byte* @out, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_hash_sha512_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha512_final(crypto_hash_sha512_state* state, byte* @out);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha512_init(crypto_hash_sha512_state* state);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_hash_sha512_statebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_hash_sha512_update(crypto_hash_sha512_state* state, byte* @in, ulong inlen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_pwhash_argon2i(byte* @out, ulong outlen, sbyte* passwd, ulong passwdlen, byte* salt, ulong opslimit, nuint memlimit, int alg);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int crypto_pwhash_argon2i_alg_argon2i13();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_bytes_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_bytes_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_memlimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_memlimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_opslimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_opslimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2i_saltbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_pwhash_argon2id(byte* @out, ulong outlen, sbyte* passwd, ulong passwdlen, byte* salt, ulong opslimit, nuint memlimit, int alg);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int crypto_pwhash_argon2id_alg_argon2id13();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_bytes_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_bytes_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_memlimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_memlimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_opslimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_opslimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_argon2id_saltbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_bytes_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_bytes_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_pwhash_scryptsalsa208sha256_ll(byte* passwd, nuint passwdlen, byte* salt, nuint saltlen, ulong N, uint r, uint p, byte* buf, nuint buflen);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_memlimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_memlimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_opslimit_max();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_opslimit_min();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_pwhash_scryptsalsa208sha256_saltbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern void randombytes_buf(void* buf, nuint size);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern void randombytes_buf(out uint buf, nuint size);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_scalarmult_curve25519(SecureMemoryHandle q, SecureMemoryHandle n, PublicKeyBytes* p);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_scalarmult_curve25519_base(PublicKeyBytes* q, SecureMemoryHandle n);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_scalarmult_curve25519_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_scalarmult_curve25519_scalarbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_secretbox_easy(byte* c, byte* m, ulong mlen, byte* n, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_secretbox_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_secretbox_macbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_secretbox_noncebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_secretbox_open_easy(byte* m, byte* c, ulong clen, byte* n, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern IntPtr crypto_secretbox_primitive();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_sign_ed25519_bytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_detached(byte* sig, out ulong siglen_p, byte* m, ulong mlen, SecureMemoryHandle sk);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_pk_to_curve25519(byte* curve25519_pk, byte* ed25519_pk);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_sign_ed25519_publickeybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_sign_ed25519_secretkeybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_seed_keypair(PublicKeyBytes* pk, SecureMemoryHandle sk, byte* seed);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_sign_ed25519_seedbytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_sk_to_curve25519(byte* curve25519_sk, byte* ed25519_sk);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_sk_to_seed(byte* seed, SecureMemoryHandle sk);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_sign_ed25519_verify_detached(byte* sig, byte* m, ulong mlen, PublicKeyBytes* pk);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_stream_chacha20_ietf(byte* c, ulong clen, byte* n, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_stream_chacha20_ietf_keybytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern nuint crypto_stream_chacha20_ietf_noncebytes();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_stream_chacha20_ietf_xor(byte* c, byte* m, ulong mlen, byte* n, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal unsafe static extern int crypto_stream_chacha20_ietf_xor_ic(byte* c, byte* m, ulong mlen, byte* n, uint ic, SecureMemoryHandle k);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern void sodium_free(IntPtr ptr);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern SecureMemoryHandle sodium_malloc(nuint size);

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int sodium_library_version_major();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern int sodium_library_version_minor();

//         [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
//         internal static extern IntPtr sodium_version_string();
//     }

// }